package com.cy.pj.sys.web.service.aspect;

import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.web.common.annotation.RequiredLog;
import com.cy.pj.sys.web.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 再Spring AOP应用中 基于@Aspect注解描述的
 * 类型为一个切面类型 此类要封装切入点几通知方法的定义
 * 1 切入点：要切入扩展业务逻辑的一些目标方法
 */
@Order(2)
@Aspect
@Component
@Slf4j
public class SysLogAspect {
    /**
     * 基于@PointCut直接定义切入点 这里的@annotation为一种切入点表达式
     * 表达由RequiredLog直接描述的方法为一个切入点方法 我们要在这样的方法上
     * 天界扩展业务逻辑
     */
    @Pointcut("@annotation(com.cy.pj.sys.web.common.annotation.RequiredLog)")
    public void doLog() {
    }

    /**
     * 应以一个通知方法 这里使用@Around进行描述 表示在此方法内部调用目标方法
     * 可以再目标方法执行之前或之后做一些扩展业务
     * @param jp 连接点 用于封装要执行的目标方法信息
     *           ProceedingJoinPoint 此类型参数只能定义在@Around定义的方法
     * @return 这里的返回值一般为切入点方法的执行结果
     * @throws Throwable
     */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("SysLogAspect.doAround.Before");
        long t1 = System.currentTimeMillis();
        log.info("Start:{}",t1);
        try {
            Object result = jp.proceed();//调用目标方法
            long t2 = System.currentTimeMillis();
            log.info("After:{}", t2);
            doSaveLogInfo(jp,t2-t1,null);
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
            long t2 = System.currentTimeMillis();
            doSaveLogInfo(jp,t2-t1,e);
            throw e;
        }
    }
    @Autowired
    private SysLogService sysLogService;
    private void doSaveLogInfo(ProceedingJoinPoint jp, long time, Throwable e)
            throws Exception {
        //1获取用户登录信息
        String username="何贤勇";//后续是系统登录用户名
        //2获取用户操作
        String ip= "202.106.0.20";//后续可以借助三方工具类

        Integer status=e==null?1:0;
        //3.1获取方法所在类的字节码对象
        Class<?> targetCls=jp.getTarget().getClass();
        //3.2获取注解描述的方法对象
        Signature signature =jp.getSignature();
        MethodSignature methodSignature= (MethodSignature) signature;
        Method targetMethod=
                targetCls.getDeclaredMethod
                        (methodSignature.getName(),methodSignature.getParameterTypes());
//        MethodSignature ms=(
//                MethodSignature) jp.getSignature();
//        Method targetMethod=
//                targetCls.getMethod(ms.getName(),ms.getParameterTypes());
        //3.3获取RequiredLog注解
        RequiredLog annotation =
                targetMethod.getAnnotation(RequiredLog.class);
        //获取用户操作
        String operation=annotation.operation();
        String classMethodName=targetCls.getName()+"."+targetMethod.getName();
        Object[]args=jp.getArgs();
        String params=new ObjectMapper().writeValueAsString(args);
        //封装用户行为日志
        SysLog sysLog=new SysLog();
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setOperation(operation);
        sysLog.setMethod(classMethodName);
        sysLog.setParams(params);
        sysLog.setStatus(status);
        sysLog.setTime(time);
        log.info("user log {}",new ObjectMapper().writeValueAsString(sysLog));
        sysLogService.saveLog(sysLog);
        if( e!=null){
            sysLog.setStatus(0);
            sysLog.setError(e.getMessage());


        }
        String userLog=new ObjectMapper()
                .writeValueAsString(sysLog);log.info("user.oper {}",userLog);
        new Thread(new Runnable() {
            @Override
            public void run() {//耗时操作
            }
        }).start();
    }
}


